package info.motteke.qjdbc.operation.select.impl;

import static info.motteke.qjdbc.dto.DtoMetaInnerAPI.*;
import info.motteke.qjdbc.Order;
import info.motteke.qjdbc.column.Meta;
import info.motteke.qjdbc.column.filter.Filter;
import info.motteke.qjdbc.db.Database;
import info.motteke.qjdbc.db.DatabaseStrategy;
import info.motteke.qjdbc.dto.DtoMeta;
import info.motteke.qjdbc.exception.NoEntitiesException;
import info.motteke.qjdbc.exec.ResultSetStatement;
import info.motteke.qjdbc.operation.select.GroupByView;
import info.motteke.qjdbc.operation.select.HavingView;
import info.motteke.qjdbc.operation.select.LimitView;
import info.motteke.qjdbc.operation.select.LockView;
import info.motteke.qjdbc.operation.select.OffsetView;
import info.motteke.qjdbc.operation.select.OrderByView;
import info.motteke.qjdbc.operation.select.Select;
import info.motteke.qjdbc.operation.select.SelectOption;
import info.motteke.qjdbc.operation.select.WhereView;
import java.util.List;

public class SelectImpl<E> {

    private final Select<E> selectView = new SelectViewImpl<E>(this);

    private final WhereView<E> whereView = new WhereViewImpl<E>(this);

    private final GroupByView<E> groupByView = new GroupByViewImpl<E>(this);

    private final HavingView<E> havingView = new HavingViewImpl<E>(this);

    private final OrderByView<E> orderByView = new OrderByViewImpl<E>(this);

    private final OffsetView<E> offsetView = new OffsetViewImpl<E>(this);

    private final LimitView<E> limitView = new LimitViewImpl<E>(this);

    private final LockView<E> lockView = new LockViewImpl<E>(this);

    private final DtoMeta<E> dtoMeta;

    private SelectOption option = new SelectOption();

    public SelectImpl(DtoMeta<E> dtoMeta) {
        this.dtoMeta = dtoMeta;
    }

    Select<E> excludesImpl(Meta... metas) {
        return selectView;
    }

    Select<E> includesImpl(Meta... metas) {
        return selectView;
    }

    Select<E> includesImpl(String expression, String name) {
        return selectView;
    }

    WhereView<E> whereImpl(Filter... filters) {
        return whereView;
    }

    GroupByView<E> groupByImpl(Meta... metas) {
        return groupByView;
    }

    HavingView<E> havingImpl(Filter... filters) {
        return havingView;
    }

    OrderByView<E> orderByImpl(Order... orders) {
        return orderByView;
    }

    OffsetView<E> offsetImpl(int offset) {
        return offsetView;
    }

    LimitView<E> limitImpl(int limit) {
        return limitView;
    }

    LockView<E> forReadOnlyImpl() {
        return lockView;
    }

    LockView<E> forUpdateImpl() {
        return lockView;
    }

    List<E> asListImpl() {
        return execute().asList();
    }

    E asSingleImpl() {
        E obj = execute().asSingle();
        if (obj == null) {
            throw new NoEntitiesException();
        } else {
            return obj;
        }
    }

    E asSingleOrNullImpl() {
        return execute().asSingle();
    }

    private ResultSetStatement<E> execute() {
        Database db = getDatabase(dtoMeta);
        DatabaseStrategy strategy = db.getStrategy();
        return strategy.createSelectSQL(option, dtoMeta);
    }
}
